package aggregation

import (
	"encoding/json"
	"gluenet/pkg/apis"
	"gluenet/pkg/meta"
	"gluenet/pkg/platform/glue"
	"gopkg.in/yaml.v3"
	"strings"
	"sync"
	"time"
)

func inGroupStatus(metric string) (string, bool) {
	var metrics = map[string]string{
		"container_state": "application_state",
	}

	label, ok := metrics[metric]

	return label, ok
}

func gluenetsApplicationStatus(c *apis.StorageContext) {
	containers, err := gluenetsApplicationContainers(c)
	if err != nil {
		c.Logger.Errorf("get application contaienrs %v", err)
		return
	}

	//c.Logger.Debugf("\n\n %v", containers)

	var data = make(map[string]map[string]interface{})
	var dvalue = make(map[string]map[string]interface{})
	c.StateCache.Range(func(key, value interface{}) bool {
		keyi := strings.Split(key.(string), "|")
		if label, ok := inGroupStatus(keyi[1]); ok {
			keyi[3] = "*"
			keyi[1] = label
			nkey := strings.Join(keyi, "|")
			odata := value.(map[string]interface{})
			if _, ok := data[nkey]; !ok {
				data[nkey] = map[string]interface{}{
					"object":    odata["object"],
					"platform":  odata["platform"],
					"time":      time.Now().Unix(),
					"label":     label,
					"guid":      odata["guid"],
					"container": "*",
				}
				dvalue[nkey] = map[string]interface{}{}
			}

			dvalue[nkey][odata["container"].(string)] = meta.StateReady
		}
		return true
	})

	//c.Logger.Debugf("\n %v ", data)
	//c.Logger.Debugf("\n %v ", dvalue)
	//c.Logger.Debugf("\n %v ", containers)

	for k, v := range data {
		var guid = strings.Split(k, "|")[2]
		var state = meta.StateReady
		for _, cname := range containers[guid] {
			var exist = false
			for cnamer := range dvalue[k] {
				if cname == cnamer {
					exist = true
				}
			}
			if !exist {
				state = meta.StateNotReady
			}
		}

		v["value"] = state
		//c.Logger.Debugf("value: %v")
	}

	//c.Logger.Debugf("data %v ", data)

	for k, vd := range data {
		var sub *sync.Map
		subi, _ := c.StatusSubscriber.LoadOrStore(k, &sync.Map{})
		sub = subi.(*sync.Map)

		bts, _ := json.Marshal(vd)
		str := string(bts)

		sub.Range(func(k, v interface{}) bool {
			mch := v.(chan string)
			mch <- str
			return true
		})

		c.StatusSubscriberTotal.Range(func(k, v interface{}) bool {
			mch := v.(chan string)
			mch <- str
			return true
		})
	}
}

func gluenetsApplicationContainers(c *apis.StorageContext) (map[string][]string, error) {
	var apps []meta.Instance
	if err := c.DataBase.InstanceList(apis.PlatfromGluenets, apis.KindApplication, &apps); err != nil {
		return nil, err
	}
	var stras []meta.Instance
	if err := c.DataBase.InstanceList(apis.PlatfromGluenets, apis.KindStrategy, &stras); err != nil {
		return nil, err
	}

	var containers = make(map[string][]string, len(apps)+len(stras))
	for _, v := range apps {
		var gins glue.Instance
		if err := json.Unmarshal([]byte(v.InstanceValue), &gins); err != nil {
			return nil, err
		}
		var cts glue.Containers
		if err := yaml.Unmarshal([]byte(gins.InstanceYaml), &cts); err != nil {
			return nil, err
		}
		for k := range cts.Containers {
			containers[v.Guid] = append(containers[v.Guid], k)
		}
	}

	for _, v := range stras {
		var gins glue.Instance
		if err := json.Unmarshal([]byte(v.InstanceValue), &gins); err != nil {
			return nil, err
		}
		var cts glue.Containers
		if err := yaml.Unmarshal([]byte(gins.InstanceYaml), &cts); err != nil {
			return nil, err
		}
		for k := range cts.Containers {
			containers[v.Guid] = append(containers[v.Guid], k)
		}
	}

	return containers, nil
}
